package cn.peach.user.service.impl;

import cn.peach.basic.exception.MyException;
import cn.peach.basic.util.AjaxResult;
import cn.peach.basic.util.HttpClientUtils;
import cn.peach.basic.util.MD5Utils;
import cn.peach.basic.util.StrUtils;
import cn.peach.user.constant.UserConstant;
import cn.peach.user.domain.Logininfo;
import cn.peach.user.domain.User;
import cn.peach.user.domain.WxUser;
import cn.peach.user.dto.LoginDto;
import cn.peach.user.mapper.LogininfoMapper;
import cn.peach.user.mapper.UserMapper;
import cn.peach.user.mapper.WxUserMapper;
import cn.peach.user.service.ILoginService;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.databind.annotation.JsonAppend;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

@Service
public class LoginServiceImpl implements ILoginService {

    @Autowired
    private LogininfoMapper logininfoMapper;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private WxUserMapper wxUserMapper;

    @Autowired
    private UserMapper userMapper;

    @Override
    public Map<String, Object> account(LoginDto loginDto) {
        // 非空校验
        if (StringUtils.isEmpty(loginDto.getPassword())
            || StringUtils.isEmpty(loginDto.getUsername())
            || StringUtils.isEmpty(loginDto.getType())){
            throw new MyException("参数不能为空");
        }
        // 根据用户名和类型查询
        Logininfo logininfo = logininfoMapper.findByUsernameAndType(loginDto);

        // 查询用户名 看是否存在
        if (logininfo == null){
            throw new MyException("用户不存在");
        }
        // 如果能查到 再来判断密码是否一致
        // 先获取密码
        String dbPwd = logininfo.getPassword();
        // 获取盐值
        String salt = logininfo.getSalt();
        // 给它加盐加密 因为之前加过要保持一致才能对比密码是否一致
        String byMd5 = MD5Utils.encrypByMd5(loginDto.getPassword() + salt);
        if (!dbPwd.equals(byMd5)){
            throw new MyException("密码错误");
        }
        // 如果没有问题 就把用户信息存进redis中
        String token = UUID.randomUUID().toString();
        // 对象存储到redis 这时需要注意到序列化问题
        redisTemplate.opsForValue().set(token, logininfo, 30, TimeUnit.MINUTES);
        // 用map来装redis中存储的用户信息和token
        Map<String, Object> map = new HashMap<>();
        map.put("token", token);
        // 隐藏用户的一些重要信息 设置为空 因为前段参数中会看到
        logininfo.setSalt(null);
        logininfo.setPassword(null);
        map.put("logininfo", logininfo);
        return map;
    }

    /**
     * 微信登录流程
     * @param params
     * @return
     */
    @Override
    public AjaxResult wechat(Map<String, String> params) {
        // 先判断code是否为空
        String code = params.get("code");
        // 是空的话返回为true*-
        if (StringUtils.isEmpty(code)){
            throw new MyException("授权码不能为空");
        }
        //  通过code获取token（令牌）  获取的是json格式的字符串  需要得到这三个参数
        String strObj = HttpClientUtils.httpGet(UserConstant.TOKEN_BY_CODE
                .replace("APPID", UserConstant.APPID)
                .replace("SECRET", UserConstant.SECRET)
                .replace("CODE", code));
        // 把json格式的字符串转换成对象
        JSONObject jsonObject = JSONObject.parseObject(strObj);
        // 获取到token令牌
        String accessToken = jsonObject.getString("access_token");
        // 获取openid
        String openId = jsonObject.getString("openid");
        // 再根据openId查询WxUser表中wxUser是否为空
        WxUser wxUser = wxUserMapper.loadByOpenId(openId);
        // wxUser不为空并且user_id也不为空  就可以免密登录
        if (wxUser != null && wxUser.getUser_id() != null){
            // 登陆成功就要像redis中logininfo保存信息  通过user_id查询logininfo
            Logininfo logininfo = logininfoMapper.findByUserId(wxUser.getUser_id());
            // 如果没有问题就把用户的信息存到redis中
            String token = UUID.randomUUID().toString();
            // 对象存进redis中时要注意序列化问题
            redisTemplate.opsForValue().set(token, logininfo, 30, TimeUnit.MINUTES);
            // 用一个map来装token和用户信息
            Map<String, Object> map = new HashMap<>();
            map.put("token", token);
            // 前端隐藏用户隐私信息
            logininfo.setSalt(null);
            logininfo.setPassword(null);
            map.put("logininfo", logininfo);
            return AjaxResult.me().subResultObj(map);
        }else {
            // 如果当前没有扫描过 返回token和openid给前端 并将token和openid拼接
            String param = "?accessToken=" + accessToken + "&openId=" + openId;
            return AjaxResult.me().setSuccess(false).subResultObj(param);
        }
    }

    @Override
    public Map<String, Object> binder(Map<String, String> param) {
        // 校验空
        String phone = param.get("phone");
        String verifyCode = param.get("verifyCode");
        String accessToken = param.get("accessToken");
        String openId = param.get("openId");
        if (StringUtils.isEmpty(phone)
                || StringUtils.isEmpty(verifyCode)
                || StringUtils.isEmpty(accessToken)
                || StringUtils.isEmpty(openId)){
            throw new MyException("参数不能为空0^0");
        }
        // 看验证码是否过期
        Object obj = redisTemplate.opsForValue().get(UserConstant.BINDER_PREFIX + phone);
        if (obj == null){
            throw new MyException("验证码已过期，请重新获取");
        // 如果没过期
        }else {
            /* 把从redis中获取的验证码与用户输入的验证码作对比*/
            String resdisCode = obj.toString().split(":")[1];
            if (!resdisCode.equalsIgnoreCase(verifyCode)){
                throw new MyException("验证码错误");
            }
        }
        // 看手机号是否注册
        User tmpUser = userMapper.getPhone(phone);
        User user = null;
        Logininfo logininfo = null;
        if (tmpUser != null){
            // 不为空 就直接绑定
            user = tmpUser;
            // 根据user表中的logininfo_id查询logininfo
            logininfo = logininfoMapper.findByUserId(user.getId());
        }else {
            // 生成user，logininfo，wx_user
            user = initUser(phone);
            logininfo = initLogininfo(user);
            // 先保存logininfo 返回自增主键
            logininfoMapper.add(logininfo);
            user.setLogininfo_id(logininfo.getId());
            userMapper.add(user);
        }
        // 获取wx_user
        String wxuser = UserConstant.WX_USER
                .replace("ACCESS_TOKEN",accessToken)
                .replace("OPENID", openId);
        // 发请求
        String strObj = HttpClientUtils.httpGet(wxuser);
        // 绑定
        WxUser wxUser = initWxUser(strObj,user);
        // 绑定成功
        wxUserMapper.add(wxUser);
        // 如果没有问题 就把用户信息存进redis中
        String token = UUID.randomUUID().toString();
        // 对象存储到redis 这时需要注意到序列化问题
        redisTemplate.opsForValue().set(token, logininfo, 30, TimeUnit.MINUTES);
        // 用map来装redis中存储的用户信息和token
        Map<String, Object> map = new HashMap<>();
        map.put("token", token);
        // 隐藏用户的一些重要信息 设置为空 因为前段参数中会看到
        logininfo.setSalt(null);
        logininfo.setPassword(null);
        map.put("logininfo", logininfo);
        return map;
    }

    /**
     * 获取wx_user
     * @param strObj
     * @param user
     * @return
     */
    private WxUser initWxUser(String strObj, User user) {
        JSONObject jsonObject = JSONObject.parseObject(strObj);
        WxUser wxUser = new WxUser();
        wxUser.setOpenid(jsonObject.getString("openid"));
        wxUser.setNickname(jsonObject.getString("nickname"));
        wxUser.setSex(jsonObject.getInteger("sex"));
        wxUser.setAddress(null);
        wxUser.setHeadimgurl(jsonObject.getString("unionid"));
        wxUser.setUser_id(user.getId());
        return wxUser;
    }

    /**
     * 根据user获取logininfo
     * @param user
     * @return
     */
    private Logininfo initLogininfo(User user) {
        Logininfo logininfo = new Logininfo();
        BeanUtils.copyProperties(user, logininfo);
        logininfo.setType(1);
        return logininfo;
    }

    /**
     * 通过手机号来获取user
     * @param phone
     * @return
     */
    private User initUser(String phone) {
        User user = new User();
        user.setUsername(phone);
        user.setPhone(phone);
        user.setState(1);
        /* 盐值  加盐加密*/
        String salt = StrUtils.getComplexRandomString(32);
        String password = MD5Utils.encrypByMd5(1 + salt);
        user.setPassword(password);
        user.setSalt(salt);
        return user;
    }
}
